home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / unix / mnx12hd / at_wini.c next >
Encoding:
C/C++ Source or Header  |  1988-01-29  |  16.0 KB  |  544 lines

  1. /* This file contains a driver for the IBM-AT winchester controller.
  2.  * It was written by Adri Koppes.
  3.  *
  4.  * The driver supports two operations: read a block and
  5.  * write a block.  It accepts two messages, one for reading and one for
  6.  * writing, both using message format m2 and with the same parameters:
  7.  *
  8.  *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
  9.  * ----------------------------------------------------------------
  10.  * |  DISK_READ | device  | proc nr |  bytes  |  offset | buf ptr |
  11.  * |------------+---------+---------+---------+---------+---------|
  12.  * | DISK_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
  13.  * ----------------------------------------------------------------
  14.  *
  15.  * The file contains one entry point:
  16.  *
  17.  *   winchester_task:    main entry when system is brought up
  18.  *
  19.  */
  20.  
  21. #include "../h/const.h"
  22. #include "../h/type.h"
  23. #include "../h/callnr.h"
  24. #include "../h/com.h"
  25. #include "../h/error.h"
  26. #include "const.h"
  27. #include "type.h"
  28. #include "proc.h"
  29.  
  30. /* I/O Ports used by winchester disk controller. */
  31.  
  32. #define WIN_REG1       0x1f0
  33. #define WIN_REG2       0x1f1
  34. #define WIN_REG3       0x1f2
  35. #define WIN_REG4       0x1f3
  36. #define WIN_REG5       0x1f4
  37. #define WIN_REG6       0x1f5
  38. #define WIN_REG7       0x1f6
  39. #define WIN_REG8       0x1f7
  40. #define WIN_REG9       0x3f6
  41.  
  42. /* Winchester disk controller command bytes. */
  43. #define WIN_RECALIBRATE    0x10    /* command for the drive to recalibrate */
  44. #define WIN_READ        0x20    /* command for the drive to read */
  45. #define WIN_WRITE       0x30    /* command for the drive to write */
  46. #define WIN_SPECIFY     0x91    /* command for the controller to accept params */
  47.  
  48. /* Parameters for the disk drive. */
  49. #define SECTOR_SIZE      512    /* physical sector size in bytes */
  50.  
  51. /* Error codes */
  52. #define ERR          -1    /* general error */
  53.  
  54. /* Miscellaneous. */
  55. #define MAX_ERRORS         4    /* how often to try rd/wt before quitting */
  56. #define NR_DEVICES        10    /* maximum number of drives */
  57. #define MAX_WIN_RETRY  10000    /* max # times to try to output to WIN */
  58. #define PART_TABLE     0x1C6    /* IBM partition table starts here in sect 0 */
  59. #define DEV_PER_DRIVE      5    /* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
  60.  
  61. /* Variables. */
  62. PRIVATE struct wini {        /* main drive struct, one entry per drive */
  63.   int wn_opcode;        /* DISK_READ or DISK_WRITE */
  64.   int wn_procnr;        /* which proc wanted this operation? */
  65.   int wn_drive;            /* drive number addressed */
  66.   int wn_cylinder;        /* cylinder number addressed */
  67.   int wn_sector;        /* sector addressed */
  68.   int wn_head;            /* head number addressed */
  69.   int wn_heads;            /* maximum number of heads */
  70.   int wn_maxsec;        /* maximum number of sectors per track */
  71.   int wn_ctlbyte;        /* control byte (steprate) */
  72.   int wn_precomp;        /* write precompensation cylinder / 4 */
  73.   long wn_low;            /* lowest cylinder of partition */
  74.   long wn_size;            /* size of partition in blocks */
  75.   int wn_count;            /* byte count */
  76.   vir_bytes wn_address;        /* user virtual address */
  77. } wini[NR_DEVICES];
  78.  
  79. PRIVATE int w_need_reset = FALSE;     /* set to 1 when controller must be reset */
  80. PRIVATE int nr_drives;         /* Number of drives */
  81.  
  82. PRIVATE message w_mess;        /* message buffer for in and out */
  83.  
  84. PRIVATE int command[8];        /* Common command block */
  85.  
  86. PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
  87.  
  88. /*===========================================================================*
  89.  *                winchester_task                     * 
  90.  *===========================================================================*/
  91. PUBLIC winchester_task()
  92. {
  93. /* Main program of the winchester disk driver task. */
  94.  
  95.   int r, caller, proc_nr;
  96.  
  97.   /* First initialize the controller */
  98.   init_param();
  99.  
  100.   /* Here is the main loop of the disk task.  It waits for a message, carries
  101.    * it out, and sends a reply.
  102.    */
  103.  
  104.   while (TRUE) {
  105.     /* First wait for a request to read or write a disk block. */
  106.     receive(ANY, &w_mess);    /* get a request to do some work */
  107.     if (w_mess.m_source < 0) {
  108.         printf("winchester task got message from %d ", w_mess.m_source);
  109.         continue;
  110.     }
  111.     caller = w_mess.m_source;
  112.     proc_nr = w_mess.PROC_NR;
  113.  
  114.     /* Now carry out the work. */
  115.     switch(w_mess.m_type) {
  116.         case DISK_READ:
  117.         case DISK_WRITE:    r = w_do_rdwt(&w_mess);    break;
  118.         default:        r = EINVAL;        break;
  119.     }
  120.  
  121.     /* Finally, prepare and send the reply message. */
  122.     w_mess.m_type = TASK_REPLY;    
  123.     w_mess.REP_PROC_NR = proc_nr;
  124.  
  125.     w_mess.REP_STATUS = r;    /* # of bytes transferred or error code */
  126.     send(caller, &w_mess);    /* send reply to caller */
  127.   }
  128. }
  129.  
  130.  
  131. /*===========================================================================*
  132.  *                w_do_rdwt                         * 
  133.  *===========================================================================*/
  134. PRIVATE int w_do_rdwt(m_ptr)
  135. message *m_ptr;            /* pointer to read or write w_message */
  136. {
  137. /* Carry out a read or write request from the disk. */
  138.   register struct wini *wn;
  139.   int r, device, errors = 0;
  140.   long sector;
  141.  
  142.   /* Decode the w_message parameters. */
  143.   device = m_ptr->DEVICE;
  144.   if (device < 0 || device >= NR_DEVICES)
  145.     return(EIO);
  146.   if (m_ptr->COUNT != BLOCK_SIZE)
  147.     return(EINVAL);
  148.   wn = &wini[device];        /* 'wn' points to entry for this drive */
  149.   wn->wn_drive = device/DEV_PER_DRIVE;    /* save drive number */
  150.   if (wn->wn_drive >= nr_drives)
  151.     return(EIO);
  152.   wn->wn_opcode = m_ptr->m_type;    /* DISK_READ or DISK_WRITE */
  153.   if (m_ptr->POSITION % BLOCK_SIZE != 0)
  154.     return(EINVAL);
  155.   sector = m_ptr->POSITION/SECTOR_SIZE;
  156.   if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size)
  157.     return(EOF);
  158.   sector += wn->wn_low;
  159.   wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec);
  160.   wn->wn_sector =  (sector % wn->wn_maxsec) + 1;
  161.   wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec;
  162.   wn->wn_count = m_ptr->COUNT;
  163.   wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
  164.   wn->wn_procnr = m_ptr->PROC_NR;
  165.  
  166.   /* This loop allows a failed operation to be repeated. */
  167.   while (errors <= MAX_ERRORS) {
  168.     errors++;        /* increment count once per loop cycle */
  169.     if (errors > MAX_ERRORS)
  170.         return(EIO);
  171.  
  172.     /* First check to see if a reset is needed. */
  173.     if (w_need_reset) w_reset();
  174.  
  175.     /* Perform the transfer. */
  176.     r = w_transfer(wn);
  177.     if (r == OK) break;    /* if successful, exit loop */
  178.  
  179.   }
  180.  
  181.   return(r == OK ? BLOCK_SIZE : EIO);
  182. }
  183.  
  184. /*===========================================================================*
  185.  *                w_transfer                     * 
  186.  *===========================================================================*/
  187. PRIVATE int w_transfer(wn)
  188. register struct wini *wn;    /* pointer to the drive struct */
  189. {
  190.   extern phys_bytes umap();
  191.   phys_bytes usr_buf = umap(proc_addr(wn->wn_procnr), D, wn->wn_address, BLOCK_SIZE);
  192.   register int i,j;
  193.   int r = 0;
  194.  
  195.   /* The command is issued by outputing 7 bytes to the controller chip. */
  196.  
  197.   if (usr_buf == (phys_bytes)0)
  198.     return(ERR);
  199.   command[0] = wn->wn_ctlbyte;
  200.   command[1] = wn->wn_precomp;
  201.   command[2] = BLOCK_SIZE/SECTOR_SIZE;
  202.   command[3] = wn->wn_sector;
  203.   command[4] = wn->wn_cylinder & 0xFF;
  204.   command[5] = ((wn->wn_cylinder & 0x0300) >> 8);
  205.   command[6] = (wn->wn_drive << 4) | wn->wn_head | 0xA0;
  206.   command[7] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
  207.  
  208.   if (com_out() != OK)
  209.     return(ERR);
  210.  
  211.   /* Block, waiting for disk interrupt. */
  212.   if (wn->wn_opcode == DISK_READ) {
  213.     for (i=0; i<BLOCK_SIZE/SECTOR_SIZE; i++) {
  214.         receive(HARDWARE, &w_mess);
  215.         lock();
  216.         dma_read((unsigned)(usr_buf >> 4), (unsigned)(usr_buf & 0x0F));
  217.         unlock();
  218.         usr_buf += 0x200;
  219.         if (win_results() != OK) {
  220.             w_need_reset = TRUE;
  221.             return(ERR);
  222.         }
  223.     }
  224.     r = OK;
  225.   } else {
  226.     for (i=0; i<MAX_WIN_RETRY && (r&8) == 0; i++)
  227.         port_in(WIN_REG8, &r);
  228.     if ((r&8) == 0) {
  229.         w_need_reset = TRUE;
  230.         return(ERR);
  231.     }
  232.     for (i=0; i<BLOCK_SIZE/SECTOR_SIZE; i++) {
  233.         lock();
  234.         dma_write((unsigned)(usr_buf >> 4), (unsigned)(usr_buf & 0x0F));
  235.         unlock();
  236.         usr_buf += 0x200;
  237.         receive(HARDWARE, &w_mess);
  238.         if (win_results() != OK) {
  239.             w_need_reset = TRUE;
  240.             return(ERR);
  241.         }
  242.     }
  243.     r = OK;
  244.   }
  245.  
  246.   if (r == ERR)
  247.     w_need_reset = TRUE;
  248.   return(r);
  249. }
  250.  
  251. /*===========================================================================*
  252.  *                w_reset                         * 
  253.  *===========================================================================*/
  254. PRIVATE w_reset()
  255. {
  256. /* Issue a reset to the controller.  This is done after any catastrophe,
  257.  * like the controller refusing to respond.
  258.  */
  259.  
  260.   int i, r;
  261.  
  262.   /* Strobe reset bit low. */
  263.   lock();
  264.   port_out(WIN_REG9, 4);
  265.   for (i = 0; i < 10; i++)
  266.      ;
  267.   port_out(WIN_REG9, wini[0].wn_ctlbyte & 0x0F);
  268.   unlock();
  269.   for (i = 0; i < MAX_WIN_RETRY && drive_busy(); i++)
  270.     ;
  271.   if (drive_busy()) {
  272.     printf("Winchester wouldn't reset, drive busy\n");
  273.     return(ERR);
  274.   }
  275.   port_in(WIN_REG2, &r);
  276.   if (r != 1) {
  277.     printf("Winchester wouldn't reset, drive error\n");
  278.     return(ERR);
  279.   }
  280.  
  281.   /* Reset succeeded.  Tell WIN drive parameters. */
  282.  
  283.   w_need_reset = FALSE;
  284.   return(win_init());
  285. }
  286.  
  287. /*===========================================================================*
  288.  *                win_init                     * 
  289.  *===========================================================================*/
  290. PRIVATE win_init()
  291. {
  292. /* Routine to initialize the drive parameters after boot or reset */
  293.  
  294.   register int i;
  295.  
  296.   command[0] = wini[0].wn_ctlbyte;
  297.   command[1] = wini[0].wn_precomp;
  298.   command[2] = wini[0].wn_maxsec;
  299.   command[4] = 0;
  300.   command[6] = (wini[0].wn_heads - 1) | 0xA0;
  301.   command[7] = WIN_SPECIFY;        /* Specify some parameters */
  302.  
  303.   if (com_out() != OK)    /* Output command block */
  304.     return(ERR);
  305.  
  306.   receive(HARDWARE, &w_mess);
  307.   if (win_results() != OK) {    /* See if controller accepted parameters */
  308.     w_need_reset = TRUE;
  309.     return(ERR);
  310.   }
  311.  
  312.   if (nr_drives > 1) {
  313.     command[0] = wini[5].wn_ctlbyte;
  314.     command[1] = wini[5].wn_precomp;
  315.     command[2] = wini[5].wn_maxsec;
  316.     command[4] = 0;
  317.     command[6] = (wini[5].wn_heads - 1) | 0xB0;
  318.     command[7] = WIN_SPECIFY;        /* Specify some parameters */
  319.  
  320.     if (com_out() != OK)            /* Output command block */
  321.         return(ERR);
  322.     receive(HARDWARE, &w_mess);
  323.     if (win_results() != OK) {  /* See if controller accepted parameters */
  324.         w_need_reset = TRUE;
  325.         return(ERR);
  326.     }
  327.   }
  328.   for (i=0; i<nr_drives; i++) {
  329.     command[0] = wini[i*5].wn_ctlbyte;
  330.     command[6] = i << 4 | 0xA0;
  331.     command[7] = WIN_RECALIBRATE;
  332.     if (com_out() != OK)
  333.         return(ERR);
  334.     receive(HARDWARE, &w_mess);
  335.     if (win_results() != OK) {
  336.         w_need_reset = TRUE;
  337.         return(ERR);
  338.     }
  339.   }
  340.   return(OK);
  341. }
  342.  
  343. /*============================================================================*
  344.  *                win_results                      *
  345.  *============================================================================*/
  346. PRIVATE win_results()
  347. {
  348. /* Routine to check if controller has done the operation succesfully */
  349.   int r;
  350.  
  351.   port_in(WIN_REG8, &r);
  352.   if ((r&0x80) != 0)
  353.     return(OK);
  354.   if ((r&0x40) == 0 || (r&0x20) != 0 || (r&0x10) == 0 || (r&1) != 0) {
  355.     if ((r&01) != 0)
  356.         port_in(WIN_REG2, &r);
  357.     return(ERR);
  358.   }
  359.   return(OK);
  360. }
  361.  
  362. /*============================================================================*
  363.  *                drive_busy                      *
  364.  *============================================================================*/
  365. PRIVATE drive_busy()
  366. {
  367. /* Wait until the controller is ready to receive a command or send status */
  368.  
  369.   register int i = 0;
  370.   int r;
  371.  
  372.   for (i = 0, r = 255; i<MAX_WIN_RETRY && (r&0x80) != 0; i++)
  373.     port_in(WIN_REG8, &r);
  374.   if ((r&0x80) != 0 || (r&0x40) == 0 || (r&0x10) == 0) {
  375.     w_need_reset = TRUE;
  376.     return(ERR);
  377.   }
  378.   return(OK);
  379. }
  380.  
  381. /*============================================================================*
  382.  *                com_out                          *
  383.  *============================================================================*/
  384. PRIVATE com_out()
  385. {
  386. /* Output the command block to the winchester controller and return status */
  387.  
  388.     register int i;
  389.     int r;
  390.  
  391.     if (drive_busy()) {
  392.         w_need_reset = TRUE;
  393.         return(ERR);
  394.     }
  395.     r = WIN_REG2;
  396.     lock();
  397.     port_out(WIN_REG9, command[0]);
  398.     for (i=1; i<8; i++, r++)
  399.         port_out(r, command[i]);
  400.     unlock();
  401.     return(OK);
  402. }
  403.  
  404. /*============================================================================*
  405.  *                init_params                      *
  406.  *============================================================================*/
  407. PRIVATE init_params()
  408. {
  409. /* This routine is called at startup to initialize the partition table,
  410.  * the number of drives and the controller
  411. */
  412.   unsigned int i, segment, offset;
  413.   phys_bytes address;
  414.   extern phys_bytes umap();
  415.   extern int vec_table[];
  416.  
  417.   /* Copy the parameter vector from the saved vector table */
  418.   offset = vec_table[2 * 0x41];
  419.   segment = vec_table[2 * 0x41 + 1];
  420.  
  421.   /* Calculate the address off the parameters and copy them to buf */
  422.   address = ((long)segment << 4) + offset;
  423.   phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 16), 16L);
  424.  
  425.   /* Copy the parameters to the structures */
  426.   copy_param(buf, &wini[0]);
  427.  
  428.   /* Copy the parameter vector from the saved vector table */
  429.   offset = vec_table[2 * 0x46];
  430.   segment = vec_table[2 * 0x46 + 1];
  431.  
  432.   /* Calculate the address off the parameters and copy them to buf */
  433.   address = ((long)segment << 4) + offset;
  434.   phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 16), 16L);
  435.  
  436.   /* Copy the parameters to the structures */
  437.   copy_param(buf, &wini[5]);
  438.  
  439.   /* Get the nummer of drives from the bios */
  440.   phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L);
  441.   nr_drives = (int) *buf;
  442.  
  443.   /* Set the parameters in the drive structure */
  444.   wini[0].wn_low = wini[5].wn_low = 0L;
  445.  
  446.   /* Initialize the controller */
  447.   if ((nr_drives > 0) && (win_init() != OK))
  448.         nr_drives = 0;
  449.  
  450.   /* Read the partition table for each drive and save them */
  451.   for (i = 0; i < nr_drives; i++) {
  452.     w_mess.DEVICE = i * 5;
  453.     w_mess.POSITION = 0L;
  454.     w_mess.COUNT = BLOCK_SIZE;
  455.     w_mess.ADDRESS = (char *) buf;
  456.     w_mess.PROC_NR = WINCHESTER;
  457.     w_mess.m_type = DISK_READ;
  458.     if (w_do_rdwt(&w_mess) != BLOCK_SIZE)
  459.         panic("Can't read partition table of winchester ", i);
  460.     if (buf[510] != 0x55 || buf[511] != 0xAA) {
  461.         printf("Invalid partition table\n");
  462.         continue;
  463.     }
  464.     copy_prt(i*5);
  465.   }
  466. }
  467.  
  468. /*============================================================================*
  469.  *                copy_params                      *
  470.  *============================================================================*/
  471. PRIVATE copy_params(src, dest)
  472. register unsigned char *src;
  473. register struct wini *dest;
  474. {
  475. /* This routine copies the parameters from src to dest
  476.  * and sets the parameters for partition 0 and 5
  477. */
  478.   register int i;
  479.   long cyl, heads, sectors;
  480.  
  481.   for (i=0; i<5; i++) {
  482.     dest[i].wn_heads = (int)src[2];
  483.     dest[i].wn_precomp = *(int *)&src[5] >> 2;
  484.     dest[i].wn_ctlbyte = (int)src[8];
  485.     dest[i].wn_maxsec = (int)src[14];
  486.   }
  487.   cyl = (long)(*(int *)src);
  488.   heads = (long)dest[0].wn_heads;
  489.   sectors = (long)dest[0].wn_maxsec;
  490.   dest[0].wn_size = cyl * heads * sectors;
  491. }
  492.  
  493. /*============================================================================*
  494.  *                copy_prt                      *
  495.  *============================================================================*/
  496. PRIVATE copy_prt(drive)
  497. int drive;
  498. {
  499. /* This routine copies the partition table for the selected drive to
  500.  * the variables wn_low and wn_size
  501.  */
  502.  
  503.   register int i, offset;
  504.   struct wini *wn;
  505.   long adjust;
  506.  
  507.   for (i=0; i<4; i++) {
  508.     adjust = 0;
  509.     wn = &wini[i + drive + 1];
  510.     offset = PART_TABLE + i * 0x10;
  511.     wn->wn_low = *(long *)&buf[offset];
  512.     if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) {
  513.         adjust = wn->wn_low;
  514.         wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE);
  515.         adjust = wn->wn_low - adjust;
  516.     }
  517.     wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust;
  518.   }
  519.   sort(&wini[drive + 1]);
  520. }
  521.  
  522. sort(wn)
  523. register struct wini *wn;
  524. {
  525.   register int i,j;
  526.  
  527.   for (i=0; i<4; i++)
  528.     for (j=0; j<3; j++)
  529.         if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0))
  530.             swap(&wn[j], &wn[j+1]);
  531.         else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)
  532.             swap(&wn[j], &wn[j+1]);
  533. }
  534.  
  535. swap(first, second)
  536. register struct wini *first, *second;
  537. {
  538.   register struct wini tmp;
  539.  
  540.   tmp = *first;
  541.   *first = *second;
  542.   *second = tmp;
  543. }
  544.